package transformnumber

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

type An2Cn struct {
	allNum    string
	numberLow map[int]string
	numberUp  map[int]string
	modeList  []string
}

func NewAn2Cn() *An2Cn {
	return &An2Cn{
		allNum:    "0123456789",
		numberLow: NUMBER_LOW_AN2CN,
		numberUp:  NUMBER_UP_AN2CN,
		modeList:  []string{"low", "up", "rmb", "direct"},
	}
}

func (a *An2Cn) An2Cn(inputs string, mode string) (string, error) {
	if inputs == "" {
		return "", errors.New("输入数据为空")
	}

	if !contains(a.modeList, mode) {
		return "", fmt.Errorf("mode 仅支持 %v", a.modeList)
	}

	// Preprocess input, 暂时不处理繁体和全角
	// inputStr = traditionalToSimplified(inputStr)
	// inputStr = fullAngleToHalfAngle(inputStr)

	if err := a.checkInputsIsValid(inputs); err != nil {
		return "", err
	}
	inputStr := inputs
	inputRune := []rune(inputs)
	sign := ""
	if inputRune[0] == '-' {
		sign = "负"
		inputStr = string(inputRune[1:])
		inputRune = inputRune[1:]
	}

	var output string
	if mode == "direct" {
		output = a.directConvert(inputStr)
	} else {
		splitResult := strings.Split(inputStr, ".")
		if len(splitResult) == 1 {
			integerData := splitResult[0]
			if mode == "rmb" {
				intPart, _ := a.integerConvert(integerData, "up")
				output = intPart + "元整"
			} else {
				output, _ = a.integerConvert(integerData, mode)
			}
		} else if len(splitResult) == 2 {
			integerData, decimalData := splitResult[0], splitResult[1]
			if mode == "rmb" {
				op, err := a.rmbConvert(integerData, decimalData)
				if err != nil {
					return "", err
				}
				output = op
			} else {
				intPart, _ := a.integerConvert(integerData, mode)
				decPart, _ := a.decimalConvert(decimalData, mode)
				output = intPart + decPart
			}
		} else {
			return "", fmt.Errorf("输入格式错误：%s", inputStr)
		}
	}

	return sign + output, nil
}

func (a *An2Cn) directConvert(inputs string) string {
	output := ""
	for _, d := range inputs {
		if d == '.' {
			output += "点"
		} else {
			output += a.numberLow[int(d)]
		}
	}
	return output
}

func (a *An2Cn) numberToString(number interface{}) string {
	switch v := number.(type) {
	case int:
		return strconv.Itoa(v)
	case float64:
		return strconv.FormatFloat(v, 'f', -1, 64)
	default:
		return fmt.Sprintf("%v", v)
	}
}

func (a *An2Cn) checkInputsIsValid(checkData string) error {
	allCheckKeys := a.allNum + ".-"
	for _, data := range checkData {
		if !strings.ContainsRune(allCheckKeys, data) {
			return fmt.Errorf("输入的数据不在转化范围内：%c", data)
		}
	}
	return nil
}

func (a *An2Cn) integerConvert(integerData string, mode string) (string, error) {
	var numeralList map[int]string
	var unitList []string
	switch mode {
	case "low":
		numeralList = NUMBER_LOW_AN2CN
		unitList = UNIT_LOW_ORDER_AN2CN
	case "up":
		numeralList = NUMBER_UP_AN2CN
		unitList = UNIT_UP_ORDER_AN2CN
	default:
		return "", fmt.Errorf("error mode: %s", mode)
	}

	// Remove leading zeros
	integerData = strings.TrimLeft(integerData, "0")

	// 计算整数部分的长度, 注意需要转换为rune后计算, 中文一个字符占一个长度
	lenIntegerData := len([]rune(integerData))
	if lenIntegerData > len(unitList) {
		return "", fmt.Errorf("超出数据范围，最长支持 %d 位", len(unitList))
	}

	var outputAn strings.Builder
	for i, d := range integerData {
		dInt := int(d)
		if dInt != 0 {
			outputAn.WriteString(numeralList[dInt] + unitList[lenIntegerData-i-1])
		} else {
			if (lenIntegerData-i-1)%4 == 0 {
				outputAn.WriteString(numeralList[dInt] + unitList[lenIntegerData-i-1])
			}
			// 如果当前位置不是第一个位置，并且前一个位置不是零，则添加零
			outputAnRune := []rune(outputAn.String())
			if i > 0 && string(outputAnRune[len(outputAnRune)-1:]) != "零" {
				outputAn.WriteString(numeralList[dInt])
			}
		}
	}

	output := outputAn.String()
	output = strings.NewReplacer(
		"零零", "零",
		"零万", "万",
		"零亿", "亿",
		"亿万", "亿",
	).Replace(output)
	output = strings.TrimRight(output, "零")

	// Solve the "一十几" problem
	if strings.HasPrefix(output, "一十") {
		output = strings.TrimPrefix(output, "一")
	}

	// Handle 0 - 1 decimal
	if output == "" {
		output = "零"
	}

	return output, nil
}

func (a *An2Cn) decimalConvert(decimalData string, oMode string) (string, error) {
	lenDecimalData := len(decimalData)

	if lenDecimalData > 16 {
		fmt.Printf("注意：小数部分长度为 %d ，将自动截取前 16 位有效精度！\n", lenDecimalData)
		decimalData = decimalData[:16]
		lenDecimalData = 16
	}

	var outputAn string
	if lenDecimalData > 0 {
		outputAn = "点"
	}

	var numeralList map[int]string
	switch oMode {
	case "low":
		numeralList = NUMBER_LOW_AN2CN
	case "up":
		numeralList = NUMBER_UP_AN2CN
	default:
		return "", fmt.Errorf("error mode: %s", oMode)
	}

	for _, data := range decimalData {
		digit := int(data)
		outputAn += numeralList[digit]
	}

	return outputAn, nil
}

func (a *An2Cn) rmbConvert(integerData, decimalData string) (string, error) {
	intData, err := a.integerConvert(integerData, "up")
	if err != nil {
		return "", err
	}
	decDataStr, err := a.decimalConvert(decimalData, "up")
	if err != nil {
		return "", err
	}
	decData := []rune(decDataStr)
	lenDecData := len(decData)

	var output string

	switch {
	case lenDecData == 0:
		output = intData + "元整"
	case lenDecData == 1:
		return "", fmt.Errorf("异常输出：%v", decData)
	case lenDecData == 2:
		if string(decData[1:2]) != "零" {
			if intData == "零" {
				output = string(decData[1:2]) + "角"
			} else {
				output = intData + "元" + string(decData[1:2]) + "角"
			}
		} else {
			output = intData + "元整"
		}
	default:
		if string(decData[1:2]) != "零" {
			if string(decData[2:3]) != "零" {
				if intData == "零" {
					output = string(decData[1:2]) + "角" + string(decData[2:3]) + "分"
				} else {
					output = intData + "元" + string(decData[1:2]) + "角" + string(decData[2:3]) + "分"
				}
			} else {
				if intData == "零" {
					output = string(decData[1:2]) + "角"
				} else {
					output = intData + "元" + string(decData[1:2]) + "角"
				}
			}
		} else {
			if string(decData[2:3]) != "零" {
				if intData == "零" {
					output = string(decData[2:3]) + "分"
				} else {
					output = intData + "元" + "零" + string(decData[2:3]) + "分"
				}
			} else {
				output = intData + "元整"
			}
		}
	}

	return output, nil
}
